package eclipse.util.maptools;
import java.awt.Color;
import java.awt.event.*;

import javax.media.opengl.*;
import javax.swing.*;
import javax.media.opengl.jgloo.jgl2d.*;
import javax.media.opengl.jgloo.jglfx.*;
import javax.media.opengl.jgloo.jglsh.*;

import eclipse.client.Database;
import eclipse.lib.graphics.*;
import eclipse.lib.map.*;
import eclipse.util.ERL;
/**
 * The Renderer Object for the map editor handles the OpenGL loop.
 * It also acts as the main loop for the Map Editor application.
 */
/*
 * Copyright (c) 2009, TOD Productions All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *	- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *	- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *	-Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
public class Renderer extends JInternalFrame implements GLEventListener, MouseMotionListener, MouseListener
{
	private static final long serialVersionUID = -6983242276460161806L;
	/** A tile used for the grid */
	public final JGLTile grid;	
	/** Attribute display */
	public final JGLText text[];
	/** Interface between JOGL and the frame */
	public final GLInterface glInterface;
	/** 
	 * true = wire frame mode on
	 * false = wireframe mode off 
	 */
	public boolean wireframe;
	/**
	 * The default constructor for the Renderer class. 
	 * This sets up the environment so that it can be used.  
	 */
	public Renderer()
	{
		//builds the grid object
		grid = new JGLTile();
		
		//set the total attribute objects to equal the number of attributes
		text = new JGLText[TileAttribute.getSize()];
		
		//Sets the size of the window to 640x480
		this.setSize(640, 480);
		//Sets the title of the window to "Map Display"
		this.setTitle("Map Display");
		//Shows the window
		this.setVisible(true);
		//set closable
		this.setClosable(true);
		//hide this window when close is clicked
		this.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
		//Links the window to JOGL
		glInterface = new GLInterface(this, this, this, this);	
		
				
	}
	/**
	 * Draws the current map.
	 * @param gl OpenGL instance
	 */
	public final void drawMap(GL gl)
	{
		//check to confirm that the MapEditor is ready to begin drawing
		if(isReady() == false)
		{
			//return because it is not ready to draw yet
			return;
		}
		//check whether all layers are being processed
		if(MapEditorApplication.displayAll)
		{
			//draw the whole map
			MapEditorApplication.map.render(gl);
		}
		//only a single layer is to be processed
		else
		{
			//draw only a single layer
			MapEditorApplication.visLayer.render(gl);
		}		
	}
	public final boolean isReady()
	{		
		//check to confirm that the layer exists
		if(MapEditorApplication.visLayer != null)
		{
			//the layer exists so return true
			return true;
		}
		//the layer did not exist so try and set the visible layer to the default (layer 0)
		else
		{
			//check to confirm that the map is loaded
			if(MapEditorApplication.map != null)
			{
				//set the map editor's default layer to layer 0
				MapEditorApplication.visLayer = MapEditorApplication.map.getLayer(0);
				//set the map editor to display all layers as default
				MapEditorApplication.displayAll = true;
				//return true since a layer now exists
				return true;
			}
			//return false because there was no map loaded
			return false;
		}
	}
	/**
	 * Draws Attributes onto the Map
	 * @param gl OpenGL instance to use
	 */
	public final void drawAttributes(GL gl)
	{
		//confirm that the MapEditor is ready to begin drawing
		if(isReady() == false)
		{
			//return because the MapEditor is not ready
			return;
		}
		//check whether the attributes should be drawn
		if(MapEditorApplication.drawAttr)
		{
			//scroll through each X value (0 to 19)
			for(int x = 0; x < 20; x++)
			{
				//scroll through each Y value (0 to 15)
				for(int y = 0; y < 15; y++)
				{
					//prepare a group to draw
					gl.glPushMatrix();
						//move the cursor to the current X-Y point
						gl.glTranslatef(32f * x, 32f * y, 0f);
						//render the text that represents the Attribute
						text[MapEditorApplication.visLayer.getAttribute(x, y).getValue()].render(gl);
					//end the group				
					gl.glPopMatrix();
				}
			}
		}		
	}
	/**
	 * Draws a grid on the map
	 * @param gl OpenGL instance
	 */
	public final void drawMapGrid(GL gl)
	{		
		//Only draw the map if wireframe is on
		if(wireframe)
		{
			//sets the glMode to draw as wireframes
			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
			//loops through the tiles across
			for(int x = 0; x < 20; x++)
			{
				//loops through the tiles down
				for(int y = 0; y < 15; y++)
				{
					//groups the next OpenGL commands
					gl.glPushMatrix();
						//move the tile to its position from the x-y point of (0,0)
						gl.glTranslatef(32f * x, 32f * y, 0f);
						//draws the tile on the new position
						grid.render(gl);
					//ends the grouping of commands
					gl.glPopMatrix();
				}
			}
			//switch to rendering as solid objects
			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL );
		}		
	}
	public final void drawLights(GL gl)
	{		
		//check to confirm that the MapEditor is ready to begin drawing
		if(isReady() == false)
		{
			//return because it is not ready to begin drawing yet
			return;
		}
		
		//confirm that lights should be drawn
		if(MapEditorApplication.useLights == false)
		{
			//lights are turned off so return
			return;
		}
		
		//confirm that the light object exists		
		if(MapEditorApplication.lightSun != null)
		{
			//it exists, so draw it
			MapEditorApplication.lightSun.render(gl);
		}			
		else
		{
			//construct the lights
			MapEditorApplication.lightSun = new JGLFilter();
			//set the filter's texture
			MapEditorApplication.lightSun.setTexture(Database.tBLANK);
			//draw only the first part
			MapEditorApplication.lightSun.setTile(0, 0);
			//set the color
			MapEditorApplication.lightSun.setColor(new JGLColor(0f, 0f, 0f, 0f));

		}
	}
	//draws the OpenGL instance
	public final void display(GLAutoDrawable glDraw) 
	{
		//Get the OpenGL object from the drawable instance
		final GL gl = glDraw.getGL();
		//Clear the screen and buffer
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);			
				
		//NOTE: these are drawn in order from bottom to top. 
		//Therefore only layers below are modified with the alpha channel of the layer above.
		
		//start the draw group
		gl.glPushMatrix();			
			//gl.glTranslatef(0f, 0f, -1f);
			//draw the map
			drawMap(gl);			
			//gl.glTranslatef(0f, 0f, 0f);
			//draw lights
			drawLights(gl);
			//draw attributes
			drawAttributes(gl);
			//draw the map grid
			drawMapGrid(gl);
		//end the draw group
		gl.glPopMatrix();
	}
	//reshape the screen if the form is changed or something
	public final void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {}
	// start the OpenGL instance
	public final void init(GLAutoDrawable glDraw)
	{
		//Get the OpenGL object from the drawable instance
		final GL gl = glDraw.getGL();
		//Set the shader mode to higher-quality
		gl.glShadeModel(GL.GL_SMOOTH);
		//Set the background color to black
		gl.glClearColor(0f, 0f, 0f, 0f);
		//Set the depth to 1f
		gl.glClearDepth(1.0f);
		//Enable the use of the z-axis
		gl.glEnable(GL.GL_DEPTH_TEST);
		//Enable using textures
		gl.glEnable(GL.GL_TEXTURE_2D);

		//eh more depth enabling
		gl.glDepthFunc(GL.GL_LEQUAL);
		//more high-quality
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
		//more high-quality
		gl.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST);
		//load the font texture
		Database.tFont = GLTextureLoader.loadIMG(new ERL(MapEditorApplication.workspace + "font").openStream(), Database.HIGH_RES, null);
		//load the blank texture, this is used as a default
		Database.tBLANK = GLTextureLoader.loadUnprocessedIMG(new ERL(MapEditorApplication.workspace + "blank").openStream(), Database.HIGH_RES , null);
		
		//load the tilesheets
		for(int i = 0; i < MapEditorApplication.TILESHEETS; i++)			
		{
			//builds the tilesheet objects 		
			Database.tTileSheet[i] = GLTextureLoader.loadIMG(new ERL(MapEditorApplication.workspace + "tiles/tiles" + i).openStream(), Database.HIGH_RES, null);		
		}
				
		//sets the texture to the grid object
		grid.setTexture(Database.tBLANK);
		//sets the draw-square of the object
		grid.setTile(0,0);
		//set the color of the grid
		grid.setColor(new JGLColor(1f, 1f, 1f, 1f));		
		
		grid.compile(gl);
		
		//scroll through each attribute
		for(int i = 0; i < text.length; i++)
		{
			//set the default text to 3 spaces
			text[i] = new JGLText("   ");
			//set the default texture to the font texture
			text[i].setTexture(Database.tFont);
		}
		//set the caption to ARENA to "Arn"
		text[TileAttribute.ARENA.getValue()].setCaption("Arn");
		//set the color of ARENA to BLACK
		text[TileAttribute.ARENA.getValue()].setColor(JGLColor.BLACK);
		//set the caption to BANK to "Bnk"
		text[TileAttribute.BANK.getValue()].setCaption("Bnk");
		//set the color of BANK to BLUE
		text[TileAttribute.BANK.getValue()].setColor(JGLColor.BLUE);
		//set the caption of BLOCK to "Blk"
		text[TileAttribute.BLOCK.getValue()].setCaption("Blk");
		//set the color of BLOCK to CYAN
		text[TileAttribute.BLOCK.getValue()].setColor(JGLColor.CYAN);
		//set the caption of CLICKABLE to "Clk"
		text[TileAttribute.CLICKABLE.getValue()].setCaption("Clk");
		//set the color of CLICKABLE to DARKGRAY
		text[TileAttribute.CLICKABLE.getValue()].setColor(JGLColor.DARKGRAY);
		//set the caption of GUILD_BLOCK to "Gbl"
		text[TileAttribute.GUILD_BLOCK.getValue()].setCaption("Gbl");
		//set the color of GUILD_BLOCK to GRAY
		text[TileAttribute.GUILD_BLOCK.getValue()].setColor(JGLColor.GRAY);
		//set the caption of HOUSE to "Hos"
		text[TileAttribute.HOUSE.getValue()].setCaption("Hos");
		//set the color of HOUSE to GREEN
		text[TileAttribute.HOUSE.getValue()].setColor(JGLColor.GREEN);
		//set the caption of ITEM to "Itm"
		text[TileAttribute.ITEM.getValue()].setCaption("Itm");
		//set the color of ITEM to LIGHTGRAY
		text[TileAttribute.HOUSE.getValue()].setColor(JGLColor.LIGHTGRAY);
		//set the caption of KEY to "Key"
		text[TileAttribute.KEY.getValue()].setCaption("Key");
		//set the color of KEY to MAGENTA
		text[TileAttribute.KEY.getValue()].setColor(JGLColor.MAGENTA);
		//set the caption of KEY_OPEN to "Kop"
		text[TileAttribute.KEY_OPEN.getValue()].setCaption("Kop");
		//set the color of KEY_OPEN to ORANGE
		text[TileAttribute.KEY_OPEN.getValue()].setColor(JGLColor.ORANGE);
		//set the caption of NONE to null String
		text[TileAttribute.NONE.getValue()].setCaption("");
		//set the caption of NPC_BLOCK to "Npc"
		text[TileAttribute.NPC_BLOCK.getValue()].setCaption("Npc");
		//set the color of NPC_BLOCK to PINK
		text[TileAttribute.NPC_BLOCK.getValue()].setColor(JGLColor.PINK);
		//set the caption of ROOF to "Rof"
		text[TileAttribute.ROOF.getValue()].setCaption("Rof");
		//set the color of ROOF to RED
		text[TileAttribute.ROOF.getValue()].setColor(JGLColor.RED);
		//set the caption of SCRIPTED to "Scr"
		text[TileAttribute.SCRIPTED.getValue()].setCaption("Scr");
		//set the color of SCRIPTED to WHITE
		text[TileAttribute.SCRIPTED.getValue()].setColor(JGLColor.WHITE);
		//set the caption of SHOP to "Shp"
		text[TileAttribute.SHOP.getValue()].setCaption("Shp");
		//set the color of SHOP to YELLOW
		text[TileAttribute.SHOP.getValue()].setColor(JGLColor.YELLOW);
		//set the caption of SIGN to "Sgn"
		text[TileAttribute.SIGN.getValue()].setCaption("Sgn");
		//set the color of SIGN to BLUE
		text[TileAttribute.SIGN.getValue()].setColor(JGLColor.BLUE);
	}
	//Reshapes the Object if the form size changes
	public final void reshape(GLAutoDrawable glDraw, int x, int y, int width, int height) 
	{
		//kept in case someone wants to use 3D 
		@SuppressWarnings("unused")
		final double left, right, bottom, top, zNear, zFar, fov, aspect;
		//45 degree FOV
		fov 	= 45d;
		//ratio of width/height
		aspect 	= (double) width / (double) height;
		//nearest possible z-point
		zNear 	= 0.1d;
		//farthest possible z-point
		zFar  	= 1000.0d;
		//top of the draw-screen			
		top 	= Math.tan(fov* Math.PI /360) * zNear;
		//bottom of the draw-screen
		bottom 	= -top;
		//left of the draw-screen
		left 	= aspect * bottom;
		//right of the draw screen
		right 	= aspect * top;
		
		//Get the GL Object
		GL gl = glDraw.getGL();
		//Draw as fast as the monitor can ONLY
		gl.setSwapInterval(1);
		//set the view-point for 2D
		gl.glViewport(0, 0, width, height);		
		gl.glMatrixMode(GL.GL_PROJECTION);
		//reset the draw-list
		gl.glLoadIdentity();
		//re-enable for 3D
//		gl.glFrustum(left, right, bottom, top, zNear, zFar);
		//disable for 3D
		gl.glOrtho(0d, 640d, 480d, 0d, 0d, 1000d);	
		gl.glMatrixMode(GL.GL_MODELVIEW);
		//reset the draw-list
		gl.glLoadIdentity();
		
	}			
	private final void processMouse(MouseEvent e)
	{		
		if(MapEditorApplication.visLayer == null)
		{
			System.out.println("layer doesnt exist");
			return;	
		}
		//the tile-x value related to where the mouse clicked
		final int pX = ((e.getX() + 16) / 32);
		//the tile-y value related to where the mouse clicked
		final int pY = ((e.getY() + 16) / 32);		
						
		//check to confirm that the tile selection is valid
		if(pX < 0 || pX > 19 || pY < 0 || pY > 14)
		{
			//it wasnt so exit before errors occur
			return;
		}					
		
		//check wheter we are editing attributes
		if(MapEditorApplication.drawAttr)
		{
			//get the byte value of the selected attribute
			byte nAttr = (byte)MapEditorApplication.selAttribute;
			//set the tile's attribute
			MapEditorApplication.visLayer.setAttribute(pX, pY, TileAttribute.getAttribute(nAttr));
			//exit
			return;
		}
		
		 //The tile that the mouse clicked
		final JGLTile tile = MapEditorApplication.visLayer.getTile(pX, pY);			
		
		tile.sheet = MapEditorApplication.selectSheet;
		//set the texture of the tile to the current texture
		tile.setTexture(
			Database.tTileSheet[MapEditorApplication.selectSheet]);
		
		//set the tile of the texture
		//if left mouse key is clicked, set the new tile
		if(e.getButton() == MouseEvent.BUTTON1 || e.getButton() == 0)
		{
			tile.setTile(
					MapEditorApplication.selectX / 32,
					MapEditorApplication.selectY / 32
			);
			//set the color of the tile
			tile.setColor(new JGLColor(MapEditorApplication.tColor));
		}
		//clear the tile if right mouse key is clicked.
		else
		{
			tile.setTile(0, 0);
			//set the color of the tile
			tile.setColor(new JGLColor(Color.WHITE));
		}	
	}
	public final void mouseDragged(MouseEvent e) 
	{		
		processMouse(e);
	}
	public final void mouseMoved(MouseEvent e) {}
	public final void mouseClicked(MouseEvent e) 
	{
		processMouse(e);	
	}
	public final void mouseEntered(MouseEvent e) {}			
	public final void mouseExited(MouseEvent e) {}
	public final void mousePressed(MouseEvent e) {}
	public final void mouseReleased(MouseEvent e) {}
}
